package com.hao.user.app.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.plugins.pagination.PageDTO;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hao.common.code.ErrorCode;
import com.hao.common.execption.BusinessException;
import com.hao.user.app.mapper.UserMapper;
import com.hao.user.app.model.dto.user.*;
import com.hao.user.app.model.entity.Address;
import com.hao.user.app.model.entity.User;
import com.hao.user.app.model.enums.LoginTypeEnum;
import com.hao.user.app.model.vo.UserVO;
import com.hao.user.app.service.AddressService;
import com.hao.user.app.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.beanutils.BeanUtils;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.DigestUtils;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.InvocationTargetException;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import static com.hao.common.constant.CommonConstant.USER_LOGIN_STATE;
import static com.hao.user.app.constant.RedisConstant.REDIS_LOCK;
import static com.hao.user.app.constant.UserConstant.ADMIN_ROLE;

/**
* @author haoge
* @description 针对表【user(用户)】的数据库操作Service实现
* @createDate 2023-05-21 09:22:40
*/
@Service
@Slf4j
public class UserServiceImpl extends ServiceImpl<UserMapper, User>
    implements UserService{

    @Resource
    private RedissonClient redissonClient;

    @Resource
    private UserMapper userMapper;

    @Resource
    private AddressService addressService;

    @Resource
    private RedisTemplate<String, Object> redisTemplate;

    /**
     * 盐值，混淆密码
     */
    private static final String SALT = "haoge";

    @Override
    public User getLoginUser() {
        // 先判断是否已登录
        Object userObj = redisTemplate.opsForValue().get(USER_LOGIN_STATE+":"+"haoge");
        User currentUser = (User) userObj;
        if (currentUser == null || currentUser.getId() == null) {
            throw new BusinessException(ErrorCode.NOT_LOGIN_ERROR);
        }
        // 从数据库查询（追求性能的话可以注释，直接走缓存）
        long userId = currentUser.getId();
        currentUser = this.getById(userId);
        if (currentUser == null) {
            throw new BusinessException(ErrorCode.NOT_LOGIN_ERROR);
        }
        return currentUser;
    }

    @Override
    public boolean userRegister(UserRegisterRequest userRegisterRequest) {
        String userAccount = userRegisterRequest.getUserAccount();
        String checkPassword = userRegisterRequest.getCheckPassword();
        String password = userRegisterRequest.getPassword();
        if (!password.equals(checkPassword)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "两次输入密码不一致");
        }
        //分布式锁
        // TODO: 2023/5/21 可以限制重试次数
        RLock lock = redissonClient.getLock(REDIS_LOCK + ":registerUser");
        try {
            while (true) {
                if (lock.tryLock(0, -1, TimeUnit.MILLISECONDS)) {
                    System.out.println("getLock:"+Thread.currentThread().getName());
                    //账号不能重复
                    QueryWrapper<User> userQueryWrapper = new QueryWrapper<>();
                    userQueryWrapper.eq("userAccount", userAccount);
                    Long count = userMapper.selectCount(userQueryWrapper);
                    if (count > 0) {
                        throw new BusinessException(ErrorCode.PARAMS_ERROR, "账号重复");
                    }
                    //加密
                    String encryptPassword = DigestUtils.md5DigestAsHex((SALT + password).getBytes());
                    //插入数据
                    User user = new User();
                    user.setUserAccount(userAccount);
                    user.setEmail(userRegisterRequest.getEmail());
                    user.setPassword(encryptPassword);
                    user.setPhone(userRegisterRequest.getPhone());
                    user.setGender(userRegisterRequest.getGender());
                    boolean result = this.save(user);
                    if (!result) {
                        throw new BusinessException(ErrorCode.SYSTEM_ERROR, "注册失败");
                    }
                    return result;
                }
            }
        }catch (InterruptedException exception) {
            log.error("抢锁失败");
            return false;
        }finally {
            //只能释放自己的锁
            if (lock.isHeldByCurrentThread()) {
                System.out.println("unLock:"+Thread.currentThread().getName());
                lock.unlock();
            }
        }
    }

    @Override
    public User userLogin(UserLoginRequest userLoginRequest, HttpServletRequest request) {

        Integer type = userLoginRequest.getType();
        LoginTypeEnum loginTypeByValue = LoginTypeEnum.getLoginTypeByValue(type);

        User user = loginTypeByValue.getLoginStrategy().login(userLoginRequest,redisTemplate,userMapper);

        if (user == null) {
            log.info("user login failed, userAccount cannot match userPassword");
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "用户不存在或密码错误");
        }
        //记录用户登录状态
        request.getSession().setAttribute(USER_LOGIN_STATE, user);
        redisTemplate.opsForValue().set(USER_LOGIN_STATE+":"+"haoge",user);
        return user;
    }

    // TODO: 2023/5/21 可以和注册用户整合  减少重复代码
    @Override
    public boolean addUser(UserAddRequest userAddRequest) {
        String userAccount = userAddRequest.getUserAccount();
        String password = userAddRequest.getPassword();
        Integer gender = userAddRequest.getGender();
        String checkPassword = userAddRequest.getCheckPassword();
        if (!password.equals(checkPassword)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "两次输入的密码不一致");
        }
        // 2. 加密
        String encryptPassword = DigestUtils.md5DigestAsHex((SALT + password).getBytes());
        RLock lock = redissonClient.getLock(REDIS_LOCK + ":addUser");
        try {
            while (true) {
                if (lock.tryLock(0, -1, TimeUnit.MILLISECONDS)) {
                    QueryWrapper<User> queryWrapper = new QueryWrapper<>();
                    queryWrapper.eq("userAccount", userAccount);
                    Long count = userMapper.selectCount(queryWrapper);
                    if (count > 0) {
                        throw new BusinessException(ErrorCode.PARAMS_ERROR, "账号重复");
                    }
                    User user = new User();
                    user.setGender(gender);
                    user.setUserAccount(userAccount);
                    user.setPassword(encryptPassword);
                    boolean result = this.save(user);
                    if (!result) {
                        throw new BusinessException(ErrorCode.SYSTEM_ERROR, "注册失败，数据库错误");
                    }
                    return result;
                }
            }
        } catch (Exception e) {
            log.error("抢锁失败");
            return false;
        }finally {
            if (lock.isHeldByCurrentThread()) {
                System.out.println("unLock:" + Thread.currentThread().getName());
                lock.unlock();
            }
        }
    }

    @Override
    public boolean userLogout(HttpServletRequest request) {
        if (request.getSession().getAttribute(USER_LOGIN_STATE) == null) {
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "未登录");
        }
        //移除登录状态
        request.getSession().removeAttribute(USER_LOGIN_STATE);
        return true;
    }

    @Override
    public boolean updateUser(UserUpdateRequest userUpdateRequest, HttpServletRequest request) {
        String password = userUpdateRequest.getPassword();
        String checkPassword = userUpdateRequest.getCheckPassword();
        Long userId = userUpdateRequest.getId();
        if (!password.equals(checkPassword)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "两次输入密码不同");
        }
        User loginUser = this.getLoginUser();
        Long loginUserId = loginUser.getId();
        String userRole = loginUser.getUserRole();

        if (!userId.equals(loginUserId) && !userRole.equals(ADMIN_ROLE)) {
            throw new BusinessException(ErrorCode.NO_AUTH_ERROR);
        }
        User user = new User();
        try {
            BeanUtils.copyProperties(user,userUpdateRequest);
        } catch (IllegalAccessException | InvocationTargetException e) {
            throw new BusinessException(ErrorCode.SYSTEM_ERROR);
        }
        // 2. 加密
        String encryptPassword = DigestUtils.md5DigestAsHex((SALT + password).getBytes());
        user.setPassword(encryptPassword);
        boolean result = this.updateById(user);
        if (!result) {
            throw new BusinessException(ErrorCode.SYSTEM_ERROR,"更新失败");
        }
        return result;

    }

    @Override
    public Page<UserVO> listUserByPage(UserQueryRequest userQueryRequest) {
        User user = new User();
        try {
            BeanUtils.copyProperties(user,userQueryRequest);
        } catch (IllegalAccessException | InvocationTargetException e) {
            throw new BusinessException(ErrorCode.SYSTEM_ERROR);
        }
        Page<User> page = new Page<>(userQueryRequest.getPageNum(), userQueryRequest.getPageSize());
        QueryWrapper<User> queryWrapper = new QueryWrapper<>(user);
        Page<User> userPage = this.page(page, queryWrapper);
        Page<UserVO> userVOPage = new PageDTO<>(userPage.getCurrent(), userPage.getSize(), userPage.getTotal());
        List<UserVO> userVOList = userPage.getRecords().stream().map(u -> {
            UserVO userVO = new UserVO();
            Long addressId = u.getAddressId();
            Address address = addressService.getById(addressId);
            StringBuilder sb = new StringBuilder();
            String s = sb.append(address.getProvince()).append(address.getCity()).append(address.getCounty()).append(address.getDetail()).toString();
            userVO.setAddress(s);
            try {
                BeanUtils.copyProperties(userVO, u);
            } catch (IllegalAccessException | InvocationTargetException e) {
                throw new BusinessException(ErrorCode.SYSTEM_ERROR);
            }
            return userVO;
        }).collect(Collectors.toList());
        userVOPage.setRecords(userVOList);
        return userVOPage;
    }

    @Override
    public boolean decreaseBalance(Long id, Double totalNum) {

        User user = this.getById(id);
        if (user.getBalance() - totalNum < 0) {
            return false;
        }
        Double subtract = user.getBalance()-totalNum;
        user.setBalance(subtract);
        return this.updateById(user);
    }

}




